#include "HIDPS2conv_kb.h"

HIDPS2conv_KB _HIDPS2conv_KB;
PS2_Client_KB *HIDPS2conv_KB::_PS2Client = nullptr;

void HIDPS2conv_KB::init(PS2_Client_KB *pPS2Client) {

    memset(KeyStatus, 0, sizeof(KeyStatus));       // 记录每个按键当前状态 0：释放， 1按下
    memset(KeyStatusTmp, 0, sizeof(KeyStatusTmp)); // 按键的新状态 0：释放， 1按下

    _PS2Client = pPS2Client;
    pPS2Client->setEventCallbacks(this);
}

/*******************************************************************************
* Function Name  : USB键盘码转PS/2扫描码（第二套）
* Description    :
* Input          : code    USB键盘码
* Output         : None
* Return         : 返回USB键盘码在PS/2扫描码中相应的序列号 ——  0：不支持

*******************************************************************************/
uint8_t HIDPS2conv_KB::usb_to_ps2_index(int keyboard_mode, uint8_t code) {
    uint8_t idx = 0;

    if (code > KEY_SET_MAX) {
        return 0;
    }

    if (keyboard_mode == 0) {
        // 0: 美式键盘
        idx = USBtoPS2_US_Index[code];
    } else if (keyboard_mode == 1) {
        idx = USBtoPS2_JIS_Index[code];
    }

    return idx;
}

/*******************************************************************************
* Function Name  : 特殊按键键盘码转PS/2扫描码（第二套）
* Description    :
* Input          : code  按键码
*   0 L ctrl
    1 L shift
    2 L Alt
    3 L WIN
    4 ctrl
    5 R shift
    6 R Alt
    7 R WIN
* Return         : 返回USB键盘码在PS/2扫描码中相应的序列号 ——  0：不支持

*******************************************************************************/
uint8_t HIDPS2conv_KB::usb_to_ps2_index_sp(uint8_t code) {
    return PS2_SET2_MAKE_SP[code];
}

void HIDPS2conv_KB::ble_onRecvDataHID(BleServerConnector *pConnector, uint8_t *dataBuf, int nLen) {
    for (int i = 0; i < HID_COUNT; i++) {
        HIDPS2conv_HIDPoint &hidPt = s_hidPoint[i];
        if (hidPt.type == 1 && hidPt.pBleServerConnector == pConnector) {
            _HIDPS2conv_KB.onRecvDataHID(i, dataBuf, nLen);
            break;
        }
    }
}

void HIDPS2conv_KB::usb_onRecvDataHID(USBClientConnector *pConnector, uint8_t *dataBuf, int nLen) {
    for (int i = 0; i < HID_COUNT; i++) {
        HIDPS2conv_HIDPoint &hidPt = s_hidPoint[i];
        if (hidPt.type == 2 && hidPt.pUSBClientConnector == pConnector) {
            _HIDPS2conv_KB.onRecvDataHID(i, dataBuf, nLen);
            break;
        }
    }
}

void HIDPS2conv_KB::onRecvDataHID(int num, uint8_t *dataBuf, int nLen) {
    HIDPS2conv::onRecvDataHID(num, dataBuf, nLen);

    HIDPS2conv_HIDPoint hidPt = s_hidPoint[num];
    HID &hid = hidPt.hid;

    int offset = 0;
    uint32_t buf;

    // 判断报表的第一字节是不是report id
    bool dataHasReportID = false;
    if (hid._ReportSize + 1 == nLen) {
        dataHasReportID = true;
        offset = 8;
    } else if (hid._ReportSize != nLen) {
        return;
    }

    memset(KeyStatusTmp, 0, sizeof(KeyStatusTmp));
    uint8_t lastKey = 0; // 记录本次报表中最后按下的按键

    for (const auto &item : hid.reportItems) {
        if (dataHasReportID && item.reportId != dataBuf[0]) {
            continue;
        }

        buf = 0;
        bincpy(&buf, dataBuf, item.bitBegin + offset, item.bitSize, 16, nLen << 3);

        if (item.usagePage == 0x07) {
            // Keyboard/Keypad
            if (item.usage >= 0xE0 && item.usage <= 0xE7) {
                // 功能键
                if (buf) {
                    lastKey = usb_to_ps2_index_sp(item.usage & 0x0F);
                    KeyStatusTmp[lastKey] = 1;
                }
            } else if (item.usage == 0x00) {
                // 普通按键
                int idx = usb_to_ps2_index(hidPt.keyboard_mode, buf);
                if (idx) {
                    lastKey = idx;
                    KeyStatusTmp[lastKey] = 1;
                }
            }
        }
    }

    uint8_t releaseKey = 0; // 记录本次报表中有没有按键被释放
    for (int i = 1; i < 120; i++) {
        if (i != lastKey) {
            if (KeyStatusTmp[i] != KeyStatus[i]) {
                if (releaseKey == 0 && KeyStatusTmp[i] == 0) {
                    releaseKey = i;
                } else {
                    _PS2Client->setKey(i, KeyStatusTmp[i]);
                }
                KeyStatus[i] = KeyStatusTmp[i];
            }
        }
    }

    // 报表里的最后的按键必须最后传给PS/2 —— 因为要设置为机打按键
    if (lastKey) {
        _PS2Client->setKey(lastKey, 1);
        KeyStatus[lastKey] = 1;
    }

    // 如果有释放按键，也必须最后传给PS/2 —— 因为要停止机打
    if (releaseKey) {
        _PS2Client->setKey(releaseKey, 0);
        KeyStatus[releaseKey] = 0;
    }
}

bool HIDPS2conv_KB::addHidDevice(BleServerConnector *pConnector) {

    HIDPS2conv_HIDPoint *hidPt = HIDPS2conv::addHidDevice(pConnector);
    if (hidPt) {
        pConnector->setBleServerConnectorCallbacks(this);
        pConnector->setOnRecvData(ble_onRecvDataHID);
        return true;
    }
    return false;
}

bool HIDPS2conv_KB::addHidDevice(USBClientConnector *pConnector) {
    HIDPS2conv_HIDPoint *hidPt = HIDPS2conv::addHidDevice(pConnector);
    if (hidPt) {
        pConnector->setUSBClientConnectorCallbacks(this);
        pConnector->setOnRecvData(usb_onRecvDataHID);

        if (_PS2Client->_PS2_KB_INFO.Enable) {
            // PS/2键盘启用状态下

            // 闪键盘灯
            uint8_t led = HID_LED_MSK_NUM_LOCK | HID_LED_MSK_CAPS_LOCK | HID_LED_MSK_SCSROLL_LOCK;
            // 全亮
            pConnector->setLED(led);
            vTaskDelay(300);
            // 全暗
            pConnector->setLED(0);
            vTaskDelay(200);
        }

        pConnector->setLED(_PS2Client->_PS2_KB_INFO.LED);

        return true;
    }
    return false;
}

void HIDPS2conv_KB::ps2kb_setLED(uint8_t param) {
    for (int i = 0; i < HID_COUNT; i++) {
        HIDPS2conv_HIDPoint &hidPt = s_hidPoint[i];
        if (hidPt.type == 1) {
            hidPt.pBleServerConnector->setLED(param);
        } else if (hidPt.type == 2) {
            hidPt.pUSBClientConnector->setLED(param);
        }
    }
}